diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 69425b735..3204244a6 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,12 +2,12 @@ @@ -24,7 +24,7 @@ ## Additional information - This PR fixes or closes issue: fixes # -- This PR is related to issue: +- This PR is related to issue: ## Checklist @@ -35,4 +35,5 @@ - [ ] The code change is tested and works locally. - [ ] Local tests pass. **Your PR cannot be merged unless tests pass** - [ ] There is no commented out code in this PR. +- [ ] UI changes including text have used i18n keys and have been added to the `en` locale. - [ ] The code has been formatted using Ruff (`ruff format frigate`) diff --git a/docs/docs/development/contributing-boards.md b/docs/docs/development/contributing-boards.md index 49a65722d..bbc435888 100644 --- a/docs/docs/development/contributing-boards.md +++ b/docs/docs/development/contributing-boards.md @@ -72,17 +72,17 @@ COPY --from=rootfs / / The images for each board will be built for each Frigate release, this is done in the `.github/workflows/ci.yml` file. The board build workflow will need to be added here. ```yml - - name: Build and push board build - uses: docker/bake-action@v3 - with: - push: true - targets: board # this is the target in the board.hcl file - files: docker/board/board.hcl # this should be updated with the actual board type - # the tags should be updated with the actual board types as well - # the community board builds should never push to cache, but it can pull from cache - set: | - board.tags=ghcr.io/${{ steps.lowercaseRepo.outputs.lowercase }}:${{ github.ref_name }}-${{ env.SHORT_SHA }}-board - *.cache-from=type=gha +- name: Build and push board build + uses: docker/bake-action@v3 + with: + push: true + targets: board # this is the target in the board.hcl file + files: docker/board/board.hcl # this should be updated with the actual board type + # the tags should be updated with the actual board types as well + # the community board builds should never push to cache, but it can pull from cache + set: | + board.tags=ghcr.io/${{ steps.lowercaseRepo.outputs.lowercase }}:${{ github.ref_name }}-${{ env.SHORT_SHA }}-board + *.cache-from=type=gha ``` ### Code Owner File diff --git a/docs/docs/development/contributing.md b/docs/docs/development/contributing.md index eb33765fe..6ef5a0fc2 100644 --- a/docs/docs/development/contributing.md +++ b/docs/docs/development/contributing.md @@ -235,3 +235,14 @@ When testing nginx config changes from within the dev container, the following c ```console sudo cp docker/main/rootfs/usr/local/nginx/conf/* /usr/local/nginx/conf/ && sudo /usr/local/nginx/sbin/nginx -s reload ``` + +## Contributing translations of the Web UI + +If you'd like to contribute translations to Frigate, please follow these steps: + +1. Fork the repository and create a new branch specifically for your translation work +2. Locate the localization files in the web/public/locales directory +3. Add or modify the appropriate language JSON files, maintaining the existing key structure while translating only the values +4. Ensure your translations maintain proper formatting, including any placeholder variables (like `{{example}}`) +5. Before submitting, thoroughly review the UI +6. When creating your PR, include a brief description of the languages you've added or updated, and reference any related issues diff --git a/web/public/locales/en/audio.json b/web/public/locales/en/audio.json index 38dea5f2c..dfe02cd92 100644 --- a/web/public/locales/en/audio.json +++ b/web/public/locales/en/audio.json @@ -1,8 +1,430 @@ { - "crying": "Crying", - "laughter": "Laughter", - "scream": "Scream", "speech": "Speech", + "babbling": "Babbling", "yell": "Yell", - "fire_alarm": "Fire alarm" + "bellow": "Bellow", + "whoop": "Whoop", + "whispering": "Whispering", + "laughter": "Laughter", + "snicker": "Snicker", + "crying": "Crying", + "sigh": "Sigh", + "singing": "Singing", + "choir": "Choir", + "yodeling": "Yodeling", + "chant": "Chant", + "mantra": "Mantra", + "child_singing": "Child Singing", + "synthetic_singing": "Synthetic Singing", + "rapping": "Rapping", + "humming": "Humming", + "groan": "Groan", + "grunt": "Grunt", + "whistling": "Whistling", + "breathing": "Breathing", + "wheeze": "Wheeze", + "snoring": "Snoring", + "gasp": "Gasp", + "pant": "Pant", + "snort": "Snort", + "cough": "Cough", + "throat_clearing": "Throat Clearing", + "sneeze": "Sneeze", + "sniff": "Sniff", + "run": "Run", + "shuffle": "Shuffle", + "footsteps": "Footsteps", + "chewing": "Chewing", + "biting": "Biting", + "gargling": "Gargling", + "stomach_rumble": "Stomach Rumble", + "burping": "Burping", + "hiccup": "Hiccup", + "fart": "Fart", + "hands": "Hands", + "finger_snapping": "Finger Snapping", + "clapping": "Clapping", + "heartbeat": "Heartbeat", + "heart_murmur": "Heart Murmur", + "cheering": "Cheering", + "applause": "Applause", + "chatter": "Chatter", + "crowd": "Crowd", + "children_playing": "Children Playing", + "animal": "Animal", + "pets": "Pets", + "dog": "Dog", + "bark": "Bark", + "yip": "Yip", + "howl": "Howl", + "bow_wow": "Bow Wow", + "growling": "Growling", + "whimper_dog": "Dog Whimper", + "cat": "Cat", + "purr": "Purr", + "meow": "Meow", + "hiss": "Hiss", + "caterwaul": "Caterwaul", + "livestock": "Livestock", + "horse": "Horse", + "clip_clop": "Clip Clop", + "neigh": "Neigh", + "cattle": "Cattle", + "moo": "Moo", + "cowbell": "Cowbell", + "pig": "Pig", + "oink": "Oink", + "goat": "Goat", + "bleat": "Bleat", + "sheep": "Sheep", + "fowl": "Fowl", + "chicken": "Chicken", + "cluck": "Cluck", + "cock_a_doodle_doo": "Cock-a-Doodle-Doo", + "turkey": "Turkey", + "gobble": "Gobble", + "duck": "Duck", + "quack": "Quack", + "goose": "Goose", + "honk": "Honk", + "wild_animals": "Wild Animals", + "roaring_cats": "Roaring Cats", + "roar": "Roar", + "bird": "Bird", + "chirp": "Chirp", + "squawk": "Squawk", + "pigeon": "Pigeon", + "coo": "Coo", + "crow": "Crow", + "caw": "Caw", + "owl": "Owl", + "hoot": "Hoot", + "flapping_wings": "Flapping Wings", + "dogs": "Dogs", + "rats": "Rats", + "mouse": "Mouse", + "patter": "Patter", + "insect": "Insect", + "cricket": "Cricket", + "mosquito": "Mosquito", + "fly": "Fly", + "buzz": "Buzz", + "frog": "Frog", + "croak": "Croak", + "snake": "Snake", + "rattle": "Rattle", + "whale_vocalization": "Whale Vocalization", + "music": "Music", + "musical_instrument": "Musical Instrument", + "plucked_string_instrument": "Plucked String Instrument", + "guitar": "Guitar", + "electric_guitar": "Electric Guitar", + "bass_guitar": "Bass Guitar", + "acoustic_guitar": "Acoustic Guitar", + "steel_guitar": "Steel Guitar", + "tapping": "Tapping", + "strum": "Strum", + "banjo": "Banjo", + "sitar": "Sitar", + "mandolin": "Mandolin", + "zither": "Zither", + "ukulele": "Ukulele", + "keyboard": "Keyboard", + "piano": "Piano", + "electric_piano": "Electric Piano", + "organ": "Organ", + "electronic_organ": "Electronic Organ", + "hammond_organ": "Hammond Organ", + "synthesizer": "Synthesizer", + "sampler": "Sampler", + "harpsichord": "Harpsichord", + "percussion": "Percussion", + "drum_kit": "Drum Kit", + "drum_machine": "Drum Machine", + "drum": "Drum", + "snare_drum": "Snare Drum", + "rimshot": "Rimshot", + "drum_roll": "Drum Roll", + "bass_drum": "Bass Drum", + "timpani": "Timpani", + "tabla": "Tabla", + "cymbal": "Cymbal", + "hi_hat": "Hi-Hat", + "wood_block": "Wood Block", + "tambourine": "Tambourine", + "rattle": "Rattle", + "maraca": "Maraca", + "gong": "Gong", + "tubular_bells": "Tubular Bells", + "mallet_percussion": "Mallet Percussion", + "marimba": "Marimba", + "glockenspiel": "Glockenspiel", + "vibraphone": "Vibraphone", + "steelpan": "Steelpan", + "orchestra": "Orchestra", + "brass_instrument": "Brass Instrument", + "french_horn": "French Horn", + "trumpet": "Trumpet", + "trombone": "Trombone", + "bowed_string_instrument": "Bowed String Instrument", + "string_section": "String Section", + "violin": "Violin", + "pizzicato": "Pizzicato", + "cello": "Cello", + "double_bass": "Double Bass", + "wind_instrument": "Wind Instrument", + "flute": "Flute", + "saxophone": "Saxophone", + "clarinet": "Clarinet", + "harp": "Harp", + "bell": "Bell", + "church_bell": "Church Bell", + "jingle_bell": "Jingle Bell", + "bicycle_bell": "Bicycle Bell", + "tuning_fork": "Tuning Fork", + "chime": "Chime", + "wind_chime": "Wind Chime", + "harmonica": "Harmonica", + "accordion": "Accordion", + "bagpipes": "Bagpipes", + "didgeridoo": "Didgeridoo", + "theremin": "Theremin", + "singing_bowl": "Singing Bowl", + "scratching": "Scratching", + "pop_music": "Pop Music", + "hip_hop_music": "Hip-Hop Music", + "beatboxing": "Beatboxing", + "rock_music": "Rock Music", + "heavy_metal": "Heavy Metal", + "punk_rock": "Punk Rock", + "grunge": "Grunge", + "progressive_rock": "Progressive Rock", + "rock_and_roll": "Rock and Roll", + "psychedelic_rock": "Psychedelic Rock", + "rhythm_and_blues": "Rhythm and Blues", + "soul_music": "Soul Music", + "reggae": "Reggae", + "country": "Country", + "swing_music": "Swing Music", + "bluegrass": "Bluegrass", + "funk": "Funk", + "folk_music": "Folk Music", + "middle_eastern_music": "Middle Eastern Music", + "jazz": "Jazz", + "disco": "Disco", + "classical_music": "Classical Music", + "opera": "Opera", + "electronic_music": "Electronic Music", + "house_music": "House Music", + "techno": "Techno", + "dubstep": "Dubstep", + "drum_and_bass": "Drum and Bass", + "electronica": "Electronica", + "electronic_dance_music": "Electronic Dance Music", + "ambient_music": "Ambient Music", + "trance_music": "Trance Music", + "music_of_latin_america": "Music of Latin America", + "salsa_music": "Salsa Music", + "flamenco": "Flamenco", + "blues": "Blues", + "music_for_children": "Music for Children", + "new-age_music": "New Age Music", + "vocal_music": "Vocal Music", + "a_capella": "A Capella", + "music_of_africa": "Music of Africa", + "afrobeat": "Afrobeat", + "christian_music": "Christian Music", + "gospel_music": "Gospel Music", + "music_of_asia": "Music of Asia", + "carnatic_music": "Carnatic Music", + "music_of_bollywood": "Music of Bollywood", + "ska": "Ska", + "traditional_music": "Traditional Music", + "independent_music": "Independent Music", + "song": "Song", + "background_music": "Background Music", + "theme_music": "Theme Music", + "jingle": "Jingle", + "soundtrack_music": "Soundtrack Music", + "lullaby": "Lullaby", + "video_game_music": "Video Game Music", + "christmas_music": "Christmas Music", + "dance_music": "Dance Music", + "wedding_music": "Wedding Music", + "happy_music": "Happy Music", + "sad_music": "Sad Music", + "tender_music": "Tender Music", + "exciting_music": "Exciting Music", + "angry_music": "Angry Music", + "scary_music": "Scary Music", + "wind": "Wind", + "rustling_leaves": "Rustling Leaves", + "wind_noise": "Wind Noise", + "thunderstorm": "Thunderstorm", + "thunder": "Thunder", + "water": "Water", + "rain": "Rain", + "raindrop": "Raindrop", + "rain_on_surface": "Rain on Surface", + "stream": "Stream", + "waterfall": "Waterfall", + "ocean": "Ocean", + "waves": "Waves", + "steam": "Steam", + "gurgling": "Gurgling", + "fire": "Fire", + "crackle": "Crackle", + "vehicle": "Vehicle", + "boat": "Boat", + "sailboat": "Sailboat", + "rowboat": "Rowboat", + "motorboat": "Motorboat", + "ship": "Ship", + "motor_vehicle": "Motor Vehicle", + "car": "Car", + "honk": "Honk", + "toot": "Toot", + "car_alarm": "Car Alarm", + "power_windows": "Power Windows", + "skidding": "Skidding", + "tire_squeal": "Tire Squeal", + "car_passing_by": "Car Passing By", + "race_car": "Race Car", + "truck": "Truck", + "air_brake": "Air Brake", + "air_horn": "Air Horn", + "reversing_beeps": "Reversing Beeps", + "ice_cream_truck": "Ice Cream Truck", + "bus": "Bus", + "emergency_vehicle": "Emergency Vehicle", + "police_car": "Police Car", + "ambulance": "Ambulance", + "fire_engine": "Fire Engine", + "motorcycle": "Motorcycle", + "traffic_noise": "Traffic Noise", + "rail_transport": "Rail Transport", + "train": "Train", + "train_whistle": "Train Whistle", + "train_horn": "Train Horn", + "railroad_car": "Railroad Car", + "train_wheels_squealing": "Train Wheels Squealing", + "subway": "Subway", + "aircraft": "Aircraft", + "aircraft_engine": "Aircraft Engine", + "jet_engine": "Jet Engine", + "propeller": "Propeller", + "helicopter": "Helicopter", + "fixed-wing_aircraft": "Fixed-Wing Aircraft", + "bicycle": "Bicycle", + "skateboard": "Skateboard", + "engine": "Engine", + "light_engine": "Light Engine", + "dental_drill's_drill": "Dental Drill", + "lawn_mower": "Lawn Mower", + "chainsaw": "Chainsaw", + "medium_engine": "Medium Engine", + "heavy_engine": "Heavy Engine", + "engine_knocking": "Engine Knocking", + "engine_starting": "Engine Starting", + "idling": "Idling", + "accelerating": "Accelerating", + "door": "Door", + "doorbell": "Doorbell", + "ding-dong": "Ding-Dong", + "sliding_door": "Sliding Door", + "slam": "Slam", + "knock": "Knock", + "tap": "Tap", + "squeak": "Squeak", + "cupboard_open_or_close": "Cupboard Open or Close", + "drawer_open_or_close": "Drawer Open or Close", + "dishes": "Dishes", + "cutlery": "Cutlery", + "chopping": "Chopping", + "frying": "Frying", + "microwave_oven": "Microwave Oven", + "blender": "Blender", + "water_tap": "Water Tap", + "sink": "Sink", + "bathtub": "Bathtub", + "hair_dryer": "Hair Dryer", + "toilet_flush": "Toilet Flush", + "toothbrush": "Toothbrush", + "electric_toothbrush": "Electric Toothbrush", + "vacuum_cleaner": "Vacuum Cleaner", + "zipper": "Zipper", + "keys_jangling": "Keys Jangling", + "coin": "Coin", + "scissors": "Scissors", + "electric_shaver": "Electric Shaver", + "shuffling_cards": "Shuffling Cards", + "typing": "Typing", + "typewriter": "Typewriter", + "computer_keyboard": "Computer Keyboard", + "writing": "Writing", + "alarm": "Alarm", + "telephone": "Telephone", + "telephone_bell_ringing": "Telephone Bell Ringing", + "ringtone": "Ringtone", + "telephone_dialing": "Telephone Dialing", + "dial_tone": "Dial Tone", + "busy_signal": "Busy Signal", + "alarm_clock": "Alarm Clock", + "siren": "Siren", + "civil_defense_siren": "Civil Defense Siren", + "buzzer": "Buzzer", + "smoke_detector": "Smoke Detector", + "fire_alarm": "Fire Alarm", + "foghorn": "Foghorn", + "whistle": "Whistle", + "steam_whistle": "Steam Whistle", + "mechanisms": "Mechanisms", + "ratchet": "Ratchet", + "clock": "Clock", + "tick": "Tick", + "tick-tock": "Tick-Tock", + "gears": "Gears", + "pulleys": "Pulleys", + "sewing_machine": "Sewing Machine", + "mechanical_fan": "Mechanical Fan", + "air_conditioning": "Air Conditioning", + "cash_register": "Cash Register", + "printer": "Printer", + "camera": "Camera", + "single-lens_reflex_camera": "Single-Lens Reflex Camera", + "tools": "Tools", + "hammer": "Hammer", + "jackhammer": "Jackhammer", + "sawing": "Sawing", + "filing": "Filing", + "sanding": "Sanding", + "power_tool": "Power Tool", + "drill": "Drill", + "explosion": "Explosion", + "gunshot": "Gunshot", + "machine_gun": "Machine Gun", + "fusillade": "Fusillade", + "artillery_fire": "Artillery Fire", + "cap_gun": "Cap Gun", + "fireworks": "Fireworks", + "firecracker": "Firecracker", + "burst": "Burst", + "eruption": "Eruption", + "boom": "Boom", + "wood": "Wood", + "chop": "Chop", + "splinter": "Splinter", + "crack": "Crack", + "glass": "Glass", + "chink": "Chink", + "shatter": "Shatter", + "silence": "Silence", + "sound_effect": "Sound Effect", + "environmental_noise": "Environmental Noise", + "static": "Static", + "white_noise": "White Noise", + "pink_noise": "Pink Noise", + "television": "Television", + "radio": "Radio", + "field_recording": "Field Recording" } diff --git a/web/public/locales/en/common.json b/web/public/locales/en/common.json index ce88e4f72..fb8222dfe 100644 --- a/web/public/locales/en/common.json +++ b/web/public/locales/en/common.json @@ -96,8 +96,8 @@ "languages": "Languages", "language": { "en": "English", - "zhCN": "简体中文(Simplified Chinese)", - "withSystem.label": "Use the system settings for languag" + "zhCN": "简体中文 (Simplified Chinese)", + "withSystem.label": "Use the system settings for language" }, "appearance": "Appearance", "darkMode": { @@ -108,13 +108,13 @@ }, "withSystem": "System", "theme": { - "label": "Theme", - "blue": "Blue", - "green": "Green", - "nord": "Nord", - "red": "Red", - "contrast": "High Contrast", - "default": "Default" + "label": "Theme", + "blue": "Blue", + "green": "Green", + "nord": "Nord", + "red": "Red", + "contrast": "High Contrast", + "default": "Default" }, "help": "Help", "documentation.label": "Frigate documentation", diff --git a/web/public/locales/en/components/auth.json b/web/public/locales/en/components/auth.json index bec032838..05c2a779f 100644 --- a/web/public/locales/en/components/auth.json +++ b/web/public/locales/en/components/auth.json @@ -1,15 +1,15 @@ { - "form": { - "user": "Username", - "password": "Password", - "login": "Login", - "errors": { - "usernameRequired": "Username is required", - "passwordRequired": "Password is required", - "rateLimit": "Exceeded rate limit. Try again later.", - "loginFailed": "Login failed", - "unknownError": "Unknown error. Check logs.", - "webUnkownError": "Unknown error. Check console logs." - } + "form": { + "user": "Username", + "password": "Password", + "login": "Login", + "errors": { + "usernameRequired": "Username is required", + "passwordRequired": "Password is required", + "rateLimit": "Exceeded rate limit. Try again later.", + "loginFailed": "Login failed", + "unknownError": "Unknown error. Check logs.", + "webUnknownError": "Unknown error. Check console logs." } -} \ No newline at end of file + } +} diff --git a/web/public/locales/en/components/camera.json b/web/public/locales/en/components/camera.json index 049384a42..c46d4fb77 100644 --- a/web/public/locales/en/components/camera.json +++ b/web/public/locales/en/components/camera.json @@ -1,8 +1,8 @@ { "group": { "label": "Camera Groups", - "add": "Add camera groups", - "edit": "Edit camera groups", + "add": "Add Camera Group", + "edit": "Edit Camera Group", "delete": { "label": "Delete Camera Group", "confirm": "Confirm Delete", diff --git a/web/public/locales/en/components/icons.json b/web/public/locales/en/components/icons.json index 22ef67e97..d9df89a30 100644 --- a/web/public/locales/en/components/icons.json +++ b/web/public/locales/en/components/icons.json @@ -1,8 +1,8 @@ { - "iconPicker": { - "selectIcon": "Select an icon", - "search": { - "placeholder": "Search for an icon..." - } + "iconPicker": { + "selectIcon": "Select an icon", + "search": { + "placeholder": "Search for an icon..." } -} \ No newline at end of file + } +} diff --git a/web/public/locales/en/components/input.json b/web/public/locales/en/components/input.json index 6cb0ea10a..7a9e35942 100644 --- a/web/public/locales/en/components/input.json +++ b/web/public/locales/en/components/input.json @@ -1,10 +1,10 @@ { - "button": { - "downloadVideo": { - "label": "Download Video", - "toast": { - "success": "Your review item video has started downloading." - } - } + "button": { + "downloadVideo": { + "label": "Download Video", + "toast": { + "success": "Your review item video has started downloading." + } } -} \ No newline at end of file + } +} diff --git a/web/public/locales/en/components/player.json b/web/public/locales/en/components/player.json index c5e9b6662..05b8dc134 100644 --- a/web/public/locales/en/components/player.json +++ b/web/public/locales/en/components/player.json @@ -1,39 +1,39 @@ { - "noRecordingsFoundForThisTime": "No recordings found for this time", - "noPreviewFound": "No Preview Found", - "noPreviewFoundFor": "No Preview Found for {{cameraName}}", - "submitFrigatePlus": { - "title": "Submit this frame to Frigate+?", - "submit": "Submit" + "noRecordingsFoundForThisTime": "No recordings found for this time", + "noPreviewFound": "No Preview Found", + "noPreviewFoundFor": "No Preview Found for {{cameraName}}", + "submitFrigatePlus": { + "title": "Submit this frame to Frigate+?", + "submit": "Submit" + }, + "livePlayerRequiredIOSVersion": "iOS 17.1 or greater is required for this live stream type.", + "streamOffline": { + "title": "Stream Offline", + "desc": "No frames have been received on the {{cameraName}} detect stream, check error logs" + }, + "cameraDisabled": "Camera is disabled", + "stats": { + "streamType": "Stream Type:", + "streamType.short": "Type", + "bandwidth": "Bandwidth:", + "bandwidth.short": "Bandwidth", + "latency": "Latency:", + "latency.short": "Latency", + "latency.value": "{{seconds}} seconds", + "latency.short.value": "{{seconds}} sec", + "totalFrames": "Total Frames:", + "droppedFrames": "Dropped Frames:", + "droppedFrames.short": "Dropped", + "droppedFrames.short.value": "{{droppedFrames}} frames", + "decodedFrames": "Decoded Frames:", + "droppedFrameRate": "Dropped Frame Rate:" + }, + "toast": { + "success": { + "submittedFrigatePlus": "Successfully submitted frame to Frigate+" }, - "livePlayerRequiredIOSVersion": "iOS 17.1 or greater is required for this live stream type.", - "streamOffline": { - "title": "Stream Offline", - "desc": "No frames have been received on the {{cameraName}} detect stream, check error logs" - }, - "cameraDisabled": "Camera is disabled", - "stats": { - "streamType": "Stream Type:", - "streamType.short": "Type", - "bandwidth": "Bandwidth:", - "bandwidth.short": "Bandwidth", - "latency": "Latency:", - "latency.short": "Latency", - "latency.value": "{{secounds}} seconds", - "latency.short.value": "{{secounds}} sec", - "totalFrames": "Total Frames:", - "droppedFrames": "Dropped Frames:", - "droppedFrames.short": "Dropped", - "droppedFrames.short.value": "{{droppedFrames}} frames", - "decodedFrames": "Decoded Frames:", - "droppedFrameRate": "Dropped Frame Rate:" - }, - "toast": { - "success": { - "submittedFrigatePlus": "Successfully submitted frame to Frigate+" - }, - "error": { - "submitFrigatePlusFailed": "Failed to submit frame to Frigate+" - } + "error": { + "submitFrigatePlusFailed": "Failed to submit frame to Frigate+" } -} \ No newline at end of file + } +} diff --git a/web/public/locales/en/objects.json b/web/public/locales/en/objects.json index 01e6d428a..130bfcc53 100644 --- a/web/public/locales/en/objects.json +++ b/web/public/locales/en/objects.json @@ -99,6 +99,22 @@ "rabbit": "Rabbit", "raccoon": "Raccoon", "robot_lawnmower": "Robot Lawnmower", - "waste_bin": "Waste bin", - "on_demand": "On_demand" + "waste_bin": "Waste Bin", + "on_demand": "On Demand", + "face": "Face", + "license_plate": "License Plate", + "package": "Package", + "bbq_grill": "BBQ Grill", + "amazon": "Amazon", + "usps": "USPS", + "ups": "UPS", + "fedex": "FedEx", + "dhl": "DHL", + "an_post": "An Post", + "purolator": "Purolator", + "postnl": "PostNL", + "nzpost": "NZPost", + "postnord": "PostNord", + "gls": "GLS", + "dpd": "DPD" } diff --git a/web/public/locales/en/views/system.json b/web/public/locales/en/views/system.json index cbcf80995..028411b19 100644 --- a/web/public/locales/en/views/system.json +++ b/web/public/locales/en/views/system.json @@ -89,8 +89,8 @@ "camera": "Camera", "unused": "Unused", "unusedStorageInformation": "Unused Storage Information", - "storageUsed": "Storage Used", - "percentageOfTotalUsed": "Percentage of Total Used", + "storageUsed": "Storage", + "percentageOfTotalUsed": "Percentage of Total", "bandwidth": "Bandwidth", "unused.tips": "This value may not accurately represent the free space available to Frigate if you have other files stored on your drive beyond Frigate's recordings. Frigate does not track storage usage outside of its recordings." } diff --git a/web/public/locales/zh-CN/common.json b/web/public/locales/zh-CN/common.json index 16944e264..28fc48d88 100644 --- a/web/public/locales/zh-CN/common.json +++ b/web/public/locales/zh-CN/common.json @@ -101,7 +101,7 @@ "systemLogs": "系统日志", "settings": "设置", "configurationEditor": "配置编辑器", - "languages": "languages / 语言", + "languages": "Languages / 语言", "language": { "en": "English", "zhCN": "简体中文", @@ -116,13 +116,13 @@ }, "withSystem": "跟随系统", "theme": { - "label": "主题", - "blue": "蓝色", - "green": "绿色", - "nord": "Nord", - "red": "红色", - "contrast": "高对比度", - "default": "默认" + "label": "主题", + "blue": "蓝色", + "green": "绿色", + "nord": "Nord", + "red": "红色", + "contrast": "高对比度", + "default": "默认" }, "help": "帮助", "documentation.label": "Frigate 的官方文档", diff --git a/web/public/locales/zh-CN/components/auth.json b/web/public/locales/zh-CN/components/auth.json index 3e9a163b7..015fa0ba8 100644 --- a/web/public/locales/zh-CN/components/auth.json +++ b/web/public/locales/zh-CN/components/auth.json @@ -1,15 +1,15 @@ { - "form": { - "user": "用户名", - "password": "密码", - "login": "登录", - "errors": { - "usernameRequired": "用户名不能为空", - "passwordRequired": "密码不能为空", - "rateLimit": "超出请求限制,请稍后再试。", - "loginFailed": "登录失败", - "unknownError": "未知错误,请检查日志。", - "webUnkownError": "未知错误,请检查控制台日志。" - } + "form": { + "user": "用户名", + "password": "密码", + "login": "登录", + "errors": { + "usernameRequired": "用户名不能为空", + "passwordRequired": "密码不能为空", + "rateLimit": "超出请求限制,请稍后再试。", + "loginFailed": "登录失败", + "unknownError": "未知错误,请检查日志。", + "webUnknownError": "未知错误,请检查控制台日志。" } -} \ No newline at end of file + } +} diff --git a/web/public/locales/zh-CN/components/dialog.json b/web/public/locales/zh-CN/components/dialog.json index 54a669f17..61729878e 100644 --- a/web/public/locales/zh-CN/components/dialog.json +++ b/web/public/locales/zh-CN/components/dialog.json @@ -83,14 +83,14 @@ }, "recording": { "confirmDelete": { - "title": "确认删除", - "desc": "您确定要删除与此审核项相关的所有录制视频吗?

提示:按住 Shift 键点击删除可跳过此对话框。", - "desc.selected": "您确定要删除与此审核项相关的所有录制视频吗?

提示:按住 Shift 键点击删除可跳过此对话框。" + "title": "确认删除", + "desc": "您确定要删除与此审核项相关的所有录制视频吗?

提示:按住 Shift 键点击删除可跳过此对话框。", + "desc.selected": "您确定要删除与此审核项相关的所有录制视频吗?

提示:按住 Shift 键点击删除可跳过此对话框。" }, "button": { - "export": "导出", - "markAsReviewed": "标记为已审核", - "deleteNow": "立即删除" + "export": "导出", + "markAsReviewed": "标记为已审核", + "deleteNow": "立即删除" } } } diff --git a/web/public/locales/zh-CN/components/icons.json b/web/public/locales/zh-CN/components/icons.json index 93b486319..ef18a8125 100644 --- a/web/public/locales/zh-CN/components/icons.json +++ b/web/public/locales/zh-CN/components/icons.json @@ -1,8 +1,8 @@ { - "iconPicker": { - "selectIcon": "选择图标", - "search": { - "placeholder": "搜索图标..." - } + "iconPicker": { + "selectIcon": "选择图标", + "search": { + "placeholder": "搜索图标..." } -} \ No newline at end of file + } +} diff --git a/web/public/locales/zh-CN/components/input.json b/web/public/locales/zh-CN/components/input.json index 3b4478076..006aca13e 100644 --- a/web/public/locales/zh-CN/components/input.json +++ b/web/public/locales/zh-CN/components/input.json @@ -1,10 +1,10 @@ { - "button": { - "downloadVideo": { - "label": "下载视频", - "toast": { - "success": "下载成功" - } - } + "button": { + "downloadVideo": { + "label": "下载视频", + "toast": { + "success": "下载成功" + } } -} \ No newline at end of file + } +} diff --git a/web/public/locales/zh-CN/components/player.json b/web/public/locales/zh-CN/components/player.json index 5b70fd856..cf1088f42 100644 --- a/web/public/locales/zh-CN/components/player.json +++ b/web/public/locales/zh-CN/components/player.json @@ -1,39 +1,39 @@ { - "noRecordingsFoundForThisTime": "找不到此次录制", - "noPreviewFound": "没有找到预览", - "noPreviewFoundFor": "没有在 {{cameraName}} 下找到预览", - "submitFrigatePlus": { - "title": "提交此帧到 Frigate+?", - "submit": "提交" + "noRecordingsFoundForThisTime": "找不到此次录制", + "noPreviewFound": "没有找到预览", + "noPreviewFoundFor": "没有在 {{cameraName}} 下找到预览", + "submitFrigatePlus": { + "title": "提交此帧到 Frigate+?", + "submit": "提交" + }, + "livePlayerRequiredIOSVersion": "此直播流类型需要 iOS 17.1 或更高版本。", + "streamOffline": { + "title": "视频流离线", + "desc": "未在 {{cameraName}} 的 detect 流上接收到任何帧,请检查错误日志" + }, + "cameraDisabled": "摄像机已禁用", + "stats": { + "streamType": "流类型:", + "streamType.short": "类型", + "bandwidth": "带宽:", + "bandwidth.short": "带宽", + "latency": "延迟:", + "latency.short": "延迟", + "latency.value": "{{seconds}} 秒", + "latency.short.value": "{{seconds}} 秒", + "totalFrames": "总帧数:", + "droppedFrames": "丢帧数:", + "droppedFrames.short": "丢帧", + "droppedFrames.short.value": "{{droppedFrames}} 帧", + "decodedFrames": "解码帧数:", + "droppedFrameRate": "丢帧率:" + }, + "toast": { + "success": { + "submittedFrigatePlus": "已成功提交帧到 Frigate+" }, - "livePlayerRequiredIOSVersion": "此直播流类型需要 iOS 17.1 或更高版本。", - "streamOffline": { - "title": "视频流离线", - "desc": "未在 {{cameraName}} 的 detect 流上接收到任何帧,请检查错误日志" - }, - "cameraDisabled": "摄像机已禁用", - "stats": { - "streamType": "流类型:", - "streamType.short": "类型", - "bandwidth": "带宽:", - "bandwidth.short": "带宽", - "latency": "延迟:", - "latency.short": "延迟", - "latency.value": "{{secounds}} 秒", - "latency.short.value": "{{secounds}} 秒", - "totalFrames": "总帧数:", - "droppedFrames": "丢帧数:", - "droppedFrames.short": "丢帧", - "droppedFrames.short.value": "{{droppedFrames}} 帧", - "decodedFrames": "解码帧数:", - "droppedFrameRate": "丢帧率:" - }, - "toast": { - "success": { - "submittedFrigatePlus": "已成功提交帧到 Frigate+" - }, - "error": { - "submitFrigatePlusFailed": "提交帧到 Frigate+ 失败" - } + "error": { + "submitFrigatePlusFailed": "提交帧到 Frigate+ 失败" } -} \ No newline at end of file + } +} diff --git a/web/src/components/auth/AuthForm.tsx b/web/src/components/auth/AuthForm.tsx index a90696dd7..dcf8eaa55 100644 --- a/web/src/components/auth/AuthForm.tsx +++ b/web/src/components/auth/AuthForm.tsx @@ -77,7 +77,7 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) { }); } } else { - toast.error(t("form.errors.webUnkownError"), { + toast.error(t("form.errors.webUnknownError"), { position: "top-center", }); } diff --git a/web/src/components/input/InputWithTags.tsx b/web/src/components/input/InputWithTags.tsx index 1f9f17626..244cc0837 100644 --- a/web/src/components/input/InputWithTags.tsx +++ b/web/src/components/input/InputWithTags.tsx @@ -74,7 +74,7 @@ export default function InputWithTags({ setSearch, allSuggestions, }: InputWithTagsProps) { - const { t } = useTranslation(["views/search"]); + const { t, i18n } = useTranslation(["views/search"]); const { data: config } = useSWR("config", { revalidateOnFocus: false, }); @@ -900,12 +900,17 @@ export default function InputWithTags({ className="cursor-pointer" onSelect={() => handleSuggestionClick(suggestion)} > - {currentFilterType - ? formatFilterValues(currentFilterType, suggestion) - : t("filter.label." + suggestion)} - {" ("} - {suggestion} - {")"} + {i18n.language === "en" ? ( + suggestion + ) : ( + <> + {suggestion} {" ("} + {currentFilterType + ? formatFilterValues(currentFilterType, suggestion) + : t("filter.label." + suggestion)} + {")"} + + )} ))} diff --git a/web/src/components/menu/SearchResultActions.tsx b/web/src/components/menu/SearchResultActions.tsx index b6d664b39..4d08332a7 100644 --- a/web/src/components/menu/SearchResultActions.tsx +++ b/web/src/components/menu/SearchResultActions.tsx @@ -104,7 +104,7 @@ export default function SearchResultActions({ download={`${searchResult.camera}_${searchResult.label}.mp4`} > - {t("itemMenu.downloadVideo")} + {t("itemMenu.downloadVideo.label")} )} diff --git a/web/src/components/overlay/detail/ObjectLifecycle.tsx b/web/src/components/overlay/detail/ObjectLifecycle.tsx index 1809ff56f..5d718f4a7 100644 --- a/web/src/components/overlay/detail/ObjectLifecycle.tsx +++ b/web/src/components/overlay/detail/ObjectLifecycle.tsx @@ -570,8 +570,12 @@ export default function ObjectLifecycle({ timezone: config.ui.timezone, strftime_fmt: config.ui.time_format == "24hour" - ? t("time.formattedTimestamp2.24hour") - : t("time.formattedTimestamp2"), + ? t("time.formattedTimestamp2.24hour", { + ns: "common", + }) + : t("time.formattedTimestamp2", { + ns: "common", + }), time_style: "medium", date_style: "medium", })} diff --git a/web/src/components/overlay/detail/SearchDetailDialog.tsx b/web/src/components/overlay/detail/SearchDetailDialog.tsx index 9bc384861..174e4660f 100644 --- a/web/src/components/overlay/detail/SearchDetailDialog.tsx +++ b/web/src/components/overlay/detail/SearchDetailDialog.tsx @@ -226,7 +226,7 @@ export default function SearchDetailDialog({ {item == "object_lifecycle" && ( )} -
{t("type.{item}")}
+
{t(`type.${item}`)}
))} @@ -310,8 +310,8 @@ function ObjectDetailsTab({ const formattedDate = useFormattedTimestamp( search?.start_time ?? 0, config?.ui.time_format == "24hour" - ? t("time.formattedTimestampWithYear.24hour") - : t("time.formattedTimestampWithYear"), + ? t("time.formattedTimestampWithYear.24hour", { ns: "common" }) + : t("time.formattedTimestampWithYear", { ns: "common" }), config?.ui.timezone, ); @@ -902,10 +902,10 @@ export function ObjectSnapshotTab({ "text-lg font-semibold leading-none tracking-tight" } > - {t("explore.submitToPlus.label")} + {t("explore.plus.submitToPlus.label")}
- {t("explore.submitToPlus.desc")} + {t("explore.plus.submitToPlus.desc")}
diff --git a/web/src/components/player/PlayerStats.tsx b/web/src/components/player/PlayerStats.tsx index 4289b1257..d792acc13 100644 --- a/web/src/components/player/PlayerStats.tsx +++ b/web/src/components/player/PlayerStats.tsx @@ -25,7 +25,7 @@ export function PlayerStats({ stats, minimal }: PlayerStatsProps) { 2 ? "text-danger" : ""}`} > - {t("stats.latency.value", { secounds: stats.latency.toFixed(2) })} + {t("stats.latency.value", { seconds: stats.latency.toFixed(2) })}

)} @@ -73,7 +73,7 @@ export function PlayerStats({ stats, minimal }: PlayerStatsProps) { className={`text-white ${stats.latency >= 2 ? "text-danger" : ""}`} > {t("stats.latency.short.value", { - secounds: stats.latency.toFixed(2), + seconds: stats.latency.toFixed(2), })} diff --git a/web/src/components/settings/CameraStreamingDialog.tsx b/web/src/components/settings/CameraStreamingDialog.tsx index f9b2ac24b..307ae2ee5 100644 --- a/web/src/components/settings/CameraStreamingDialog.tsx +++ b/web/src/components/settings/CameraStreamingDialog.tsx @@ -290,14 +290,18 @@ export function CameraStreamingDialog({ - {t("group.camera.setting.streamMethod.method.noStreaming")} + {t( + "group.camera.setting.streamMethod.method.noStreaming.label", + )} - {t("group.camera.setting.streamMethod.method.smartStreaming")} + {t( + "group.camera.setting.streamMethod.method.smartStreaming.label", + )} {t( - "group.camera.setting.streamMethod.method.continuousStreaming", + "group.camera.setting.streamMethod.method.continuousStreaming.label", )} @@ -344,7 +348,7 @@ export function CameraStreamingDialog({ htmlFor="compatibility" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" > - {t("group.camera.setting.compatibilityMode")} + {t("group.camera.setting.compatibilityMode.label")}
diff --git a/web/src/components/ui/calendar-range.tsx b/web/src/components/ui/calendar-range.tsx index e360010b4..3c2974634 100644 --- a/web/src/components/ui/calendar-range.tsx +++ b/web/src/components/ui/calendar-range.tsx @@ -12,10 +12,7 @@ import { import { Switch } from "./switch"; import { cn } from "@/lib/utils"; import { LuCheck } from "react-icons/lu"; -import { useTranslation } from "react-i18next"; - -const { t } = useTranslation(["common"]); - +import { t } from "i18next"; export interface DateRangePickerProps { /** Click handler for applying the updates from DateRangePicker. */ @@ -433,7 +430,7 @@ export function DateRangePicker({ } }} > - {t("button.apply", { ns: "common"})} + {t("button.apply", { ns: "common" })}
diff --git a/web/src/components/ui/pagination.tsx b/web/src/components/ui/pagination.tsx index 6d7a25073..439344fc3 100644 --- a/web/src/components/ui/pagination.tsx +++ b/web/src/components/ui/pagination.tsx @@ -1,11 +1,9 @@ -import * as React from "react" -import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" +import * as React from "react"; +import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"; -import { cn } from "@/lib/utils" -import { ButtonProps, buttonVariants } from "@/components/ui/button" -import { useTranslation } from "react-i18next" - -const { t } = useTranslation(["common"]) +import { cn } from "@/lib/utils"; +import { ButtonProps, buttonVariants } from "@/components/ui/button"; +import { t } from "i18next"; const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (