Skip to content

Commit eb4bef6

Browse files
committed
initial commit
0 parents  commit eb4bef6

29 files changed

+1269
-0
lines changed

.editorconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# editorconfig.org
2+
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
end_of_line = lf
8+
indent_size = 4
9+
indent_style = space
10+
insert_final_newline = true
11+
trim_trailing_whitespace = true
12+
13+
[{compose.yaml,compose.*.yaml}]
14+
indent_size = 2
15+
16+
[*.md]
17+
trim_trailing_whitespace = false

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/tests export-ignore

.github/workflows/ci.yml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
pull_request:
6+
schedule:
7+
- cron: '0 0 1,16 * *'
8+
9+
jobs:
10+
tests:
11+
name: PHP ${{ matrix.php }}, SF ${{ matrix.symfony }} - ${{ matrix.deps }}
12+
runs-on: ubuntu-latest
13+
strategy:
14+
matrix:
15+
php: [ 8.2, 8.3, 8.4 ]
16+
deps: [ highest ]
17+
symfony: [ 6.4.*, 7.3.*, 7.4.* ]
18+
include:
19+
- php: 8.2
20+
deps: lowest
21+
symfony: '*'
22+
steps:
23+
- name: Checkout code
24+
uses: actions/checkout@v4
25+
26+
- name: Setup PHP
27+
uses: shivammathur/setup-php@v2
28+
with:
29+
php-version: ${{ matrix.php }}
30+
coverage: none
31+
tools: flex
32+
33+
- name: Install dependencies
34+
uses: ramsey/composer-install@v3
35+
with:
36+
dependency-versions: ${{ matrix.deps }}
37+
composer-options: --prefer-dist
38+
env:
39+
SYMFONY_REQUIRE: ${{ matrix.symfony }}
40+
41+
- name: Test
42+
run: vendor/bin/phpunit
43+
44+
php-stan:
45+
name: PHPStan
46+
runs-on: ubuntu-latest
47+
steps:
48+
- name: Checkout code
49+
uses: actions/checkout@v4
50+
51+
- name: Setup PHP
52+
uses: shivammathur/setup-php@v2
53+
with:
54+
php-version: 8.4
55+
56+
- name: Install dependencies
57+
uses: ramsey/composer-install@v3
58+
59+
- name: PHPStan
60+
run: vendor/bin/phpstan analyse
61+
62+
php-cs-fixer:
63+
name: PHP-CS-Fixer
64+
runs-on: ubuntu-latest
65+
steps:
66+
- name: Checkout code
67+
uses: actions/checkout@v4
68+
69+
- name: Setup PHP
70+
uses: shivammathur/setup-php@v2
71+
with:
72+
php-version: 8.2
73+
74+
- name: Install dependencies
75+
uses: ramsey/composer-install@v3
76+
77+
- name: PHP-CS-Fixer
78+
run: vendor/bin/php-cs-fixer fix --dry-run --diff

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
vendor/
2+
var/
3+
composer.lock
4+
.phpunit.result.cache
5+
.php-cs-fixer.cache

.php-cs-fixer.dist.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
return (new PhpCsFixer\Config())
4+
->setRules([
5+
'@Symfony' => true,
6+
])
7+
->setFinder(
8+
(new PhpCsFixer\Finder())
9+
->in([__DIR__.'/src', __DIR__.'/tests'])
10+
)
11+
;

LICENSE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) SymfonyCasts <https://symfonycasts.com/>
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

README.md

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
# symfonycasts/object-translation-bundle
2+
3+
This bundle provides a simple way to translate Doctrine entities in Symfony applications.
4+
5+
## Installation
6+
7+
### Install the bundle via Composer:
8+
9+
```bash
10+
composer require symfonycasts/object-translation-bundle
11+
```
12+
13+
### Enable the bundle in your `config/bundles.php` file:
14+
15+
> [!NOTE]
16+
> This step is not required if you are using Symfony Flex.
17+
18+
```php
19+
return [
20+
// ...
21+
ObjectTranslationBundle::class => ['all' => true],
22+
];
23+
```
24+
25+
### Create the translation entity in your app:
26+
27+
> [!NOTE]
28+
> This step is not required if you are using Symfony Flex.
29+
30+
```php
31+
namespace App\Entity;
32+
33+
use Doctrine\ORM\Mapping as ORM;
34+
use SymfonyCasts\ObjectTranslationBundle\Model\Translation as BaseTranslation;
35+
36+
#[ORM\Entity]
37+
class Translation extends BaseTranslation
38+
{
39+
#[ORM\Id]
40+
#[ORM\GeneratedValue]
41+
#[ORM\Column]
42+
public int $id;
43+
}
44+
```
45+
46+
### Configure the entity in your `config/packages/object_translation.yaml` file:
47+
48+
> [!NOTE]
49+
> This step is not required if you are using Symfony Flex.
50+
51+
```yaml
52+
symfonycasts_object_translation:
53+
translation_class: App\Entity\Translation
54+
```
55+
56+
### Create and run the migration to add the translation table:
57+
58+
```bash
59+
symfony console make:migration
60+
symfony console doctrine:migrations:migrate
61+
```
62+
63+
## Marking Entities as Translatable
64+
65+
To mark an entity as translatable, use the `Translatable` attribute on the entity class
66+
and the `TranslatableProperty` attribute on the fields you want to translate.
67+
68+
```php
69+
namespace App\Entity;
70+
71+
use Doctrine\ORM\Mapping as ORM;
72+
use SymfonyCasts\ObjectTranslationBundle\Mapping\Translatable;
73+
use SymfonyCasts\ObjectTranslationBundle\Mapping\TranslatableProperty;
74+
75+
#[ORM\Entity]
76+
#[Translatable('product')]
77+
class Product
78+
{
79+
// ...
80+
81+
#[ORM\Column(type: 'string', length: 255)]
82+
#[TranslatableProperty]
83+
public string $name;
84+
85+
#[ORM\Column(type: 'text')]
86+
#[TranslatableProperty]
87+
public string $description;
88+
}
89+
```
90+
91+
## Usage
92+
93+
### `ObjectTranslator` Service
94+
95+
You can inject the `ObjectTranslator` service to translate entities.
96+
97+
```php
98+
use SymfonyCasts\ObjectTranslationBundle\ObjectTranslator;
99+
100+
class ProductController
101+
{
102+
public function show(Product $product, ObjectTranslator $objectTranslator)
103+
{
104+
// translate into the current request locale
105+
$translatedProduct = $objectTranslator->translate($product);
106+
107+
$translatedProduct->getName(); // returns the translated name (if available)
108+
$translatedProduct->getDescription(); // returns the translated description (if available)
109+
110+
// ...
111+
}
112+
}
113+
```
114+
115+
The second argument of the `translate()` method allows you to specify a locale:
116+
117+
```php
118+
$product = $objectTranslator->translate($product, 'fr'); // translates into French
119+
```
120+
121+
### `translate_object` Twig Filter
122+
123+
If using Twig, you can use the `translate_object` filter to translate entities directly in templates.
124+
125+
```twig
126+
{% set translatedProduct = product|translate_object %} {# translates into the current request locale #}
127+
128+
{% set frenchProduct = product|translate_object('fr') %} {# translates into French #}
129+
```
130+
131+
## Managing Translations
132+
133+
The `Translation` database table has the following structure:
134+
135+
- `id`: Primary key (added by you)
136+
- `object_type`: The *alias* defined in the `Translatable` attribute (e.g., `product`)
137+
- `object_id`: The ID of the translated entity
138+
- `locale`: The locale of the translation (e.g., `fr`)
139+
- `field`: The entity property name being translated (e.g., `description`)
140+
- `value`: The translated value
141+
142+
Each row represents a single property translation for a specific entity in a specific locale.
143+
144+
You can manage these translations yourself but two console commands are provided to help:
145+
146+
### `object-translation:export`
147+
148+
This command exports all entity translations, in your default locale, to a CSV file.
149+
150+
```bash
151+
symfony console object-translation:export translations.csv
152+
```
153+
154+
This will create a `translations.csv` file at the root of your project with the following structure:
155+
156+
```csv
157+
type,id,field,value
158+
```
159+
160+
You can then take this file to translation service for translation. Be sure to keep
161+
the `type`, `id`, and `field` columns intact. The `value` column is what needs to be translated
162+
into the desired language.
163+
164+
### `object-translation:import`
165+
166+
This command imports translations from a CSV file created by the `export` command after
167+
the `value` column has been translated.
168+
169+
```bash
170+
symfony console object-translation:import translations_fr.csv fr
171+
```
172+
173+
The first argument is the path to the CSV file, and the second argument is the locale
174+
of the translations in that file.
175+
176+
## Translation Caching
177+
178+
For performance, translations are cached. By default, they use your `cache.app` pool
179+
and have no expiration time. This can be configured:
180+
181+
```yaml
182+
symfonycasts_object_translation:
183+
cache:
184+
pool: 'cache.object_translation' # a custom pool name
185+
ttl: 3600 # expire after one hour
186+
```
187+
188+
### Translation Tags
189+
190+
If your cache pool supports *cache tagging*, tags are added to the cache keys. Two keys
191+
are added:
192+
193+
- `object-translation`: All translations are tagged with this key.
194+
- `object-translation-{type}`: Where `{type}` is the translatable alias (e.g., `product`).
195+
196+
You can invalidate these tags by using the `cache:pool:invalidate-tags` command:
197+
198+
```bash
199+
# invalidate all object translation caches
200+
symfony console cache:pool:invalidate-tags object-translation
201+
202+
# invalidate only the translation cache for "product" entities
203+
symfony console cache:pool:invalidate-tags object-translation-product
204+
```
205+
206+
### `object-translation:warmup` Command
207+
208+
This command preloads all translations into the cache for all your
209+
app's enabled locales.
210+
211+
```bash
212+
symfony console object-translation:warmup
213+
```
214+
215+
## Full Default Configuration
216+
217+
```yaml
218+
symfonycasts_object_translation:
219+
220+
# The class name of your translation entity.
221+
translation_class: ~ # Required, Example: App\Entity\Translation
222+
223+
# Cache settings for object translations.
224+
cache:
225+
enabled: true
226+
227+
# The cache pool to use for storing object translations.
228+
pool: cache.app
229+
230+
# The time-to-livefor cached translations, in seconds, null for no expiration.
231+
ttl: null
232+
```

0 commit comments

Comments
 (0)